﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.Data;

namespace StyleMVVM.Validation.Impl
{
	internal class EnumerableValidationContext : BaseValidationContext
	{
		private List<IInternalValidationContext> enumerableContexts = new List<IInternalValidationContext>();

		public EnumerableValidationContext(object validationObject,
		                                   string[] standards,
		                                   IValidationService validationService,
		                                   IValidationStandardsSelector standardsSelector,
		                                   IReflectionService reflectionService,
		                                   bool monitor,
		                                   bool shared)
			: base(validationObject, standards, validationService, standardsSelector, reflectionService, monitor, shared)
		{
			if (monitor)
			{
				INotifyCollectionChanged validationCollection
					= validationObject as INotifyCollectionChanged;

				if (validationCollection != null)
				{
					validationCollection.CollectionChanged += ValidationCollectionOnCollectionChanged;
				}
			}
		}

		protected override void SetupRulesAndProperties(string[] standards)
		{
			base.SetupRulesAndProperties(standards);

			foreach (IInternalValidationContext internalValidationContext in enumerableContexts)
			{
				if (internalValidationContext != null)
				{
					internalValidationContext.Standards = standards;
				}
			}
		}

		private void ValidationCollectionOnCollectionChanged(object sender,
		                                                     NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
		{
			IEnumerable enumerable = ValidationObject as IEnumerable;

			if (enumerable != null)
			{
				if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Reset)
				{
					foreach (IInternalValidationContext internalValidationContext in enumerableContexts)
					{
						internalValidationContext.SetParent(null, "Item");
					}

					enumerableContexts.Clear();

					foreach (object validationObject in enumerable)
					{
						IInternalValidationContext internalValidationContext =
							validationService.GetValidationContext(validationObject, monitor, shared) as
							IInternalValidationContext;

						if (internalValidationContext != null)
						{
							internalValidationContext.SetParent(this, "Item");

							enumerableContexts.Add(internalValidationContext);
						}
					}
				}
				else
				{
					if (notifyCollectionChangedEventArgs.OldItems != null)
					{
						foreach (object oldItem in notifyCollectionChangedEventArgs.OldItems)
						{
							IInternalValidationContext oldContext = enumerableContexts.FirstOrDefault(x => x.ValidationObject == oldItem);

							if (oldContext != null)
							{
								enumerableContexts.Remove(oldContext);

								oldContext.SetParent(null, "Item");
							}
						}
					}

					if (notifyCollectionChangedEventArgs.NewItems != null)
					{
						foreach (object newItem in notifyCollectionChangedEventArgs.NewItems)
						{
							IInternalValidationContext internalValidationContext =
								validationService.GetValidationContext(newItem, monitor, shared) as IInternalValidationContext;

							if (internalValidationContext != null)
							{
								internalValidationContext.SetParent(this, "Item");

								enumerableContexts.Add(internalValidationContext);
							}
						}
					}
				}
			}
		}

		public override IEnumerable<IValidationContext> Children
		{
			get
			{
				List<IValidationContext> returnValue = new List<IValidationContext>(base.Children);

				returnValue.AddRange(enumerableContexts);

				return returnValue;
			}
		}

		public override void AddRules(IEnumerable<IValidationRule> validationRules)
		{
			foreach (IValidationContext validationContext in Children)
			{
				validationContext.AddRules(validationRules);
			}
		}

		public override void RemoveRules(IEnumerable<IValidationRule> validationRules)
		{
			foreach (IValidationContext validationContext in Children)
			{
				validationContext.RemoveRules(validationRules);
			}
		}

		public override void Validate()
		{
			IEnumerable enumerable = ValidationObject as IEnumerable;

			if (enumerable != null)
			{
				if (enumerableContexts.Count == 0)
				{
					foreach (object validationObject in enumerable)
					{
						IInternalValidationContext internalValidationContext =
							validationService.GetValidationContext(validationObject, monitor, shared) as
							IInternalValidationContext;

						if (internalValidationContext != null)
						{
							internalValidationContext.SetParent(this, "Item");

							enumerableContexts.Add(internalValidationContext);
						}
					}
				}
				else
				{
					foreach (object validationObject in enumerable)
					{
						IInternalValidationContext currentContext =
							enumerableContexts.FirstOrDefault(x => x.ValidationObject == validationObject);

						if (currentContext == null)
						{
							IInternalValidationContext internalValidationContext =
								validationService.GetValidationContext(validationObject, monitor, shared) as
								IInternalValidationContext;

							if (internalValidationContext != null)
							{
								internalValidationContext.SetParent(this, "Item");

								enumerableContexts.Add(internalValidationContext);
							}
						}
					}
				}
			}

			foreach (IInternalValidationContext internalValidationContext in enumerableContexts)
			{
				internalValidationContext.Validate();
			}
		}
	}
}